// ATASHD3D.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "Captor.h"
#include "CaptorManager.h"
//#include "DeviceHooker.h"

extern CCaptorManager g_mgr;

CCaptor::CCaptor(CCaptorManager* pmgr)
{
	m_pmgr = pmgr;
	m_hMutex = NULL;
	m_hFile = NULL;
	m_hFileMapping = NULL;
	m_pcap = NULL;
	m_hrgn = NULL;
	m_pSwapChain = NULL;
	m_pDeviceHooker = NULL;
	m_pDXGISwapChain = NULL;
	m_pTexture = NULL;
	m_pDevice = NULL;
	m_dwLastCaptureTime = 0;
}

CCaptor::~CCaptor()
{
	DebugInfoTempA("delete ccaptor.");
	Destroy();
}

BOOL CCaptor::Create(LPCSTR szDeviceName)
{
	if (strlen(szDeviceName) + 1 > sizeof(m_szDeviceName) / sizeof(m_szDeviceName[0]))
		return FALSE;

	strcpy_s(m_szDeviceName, szDeviceName);

	m_hMutex = CreateMutex(NULL, FALSE, NULL);
	if (!m_hMutex)
		return FALSE;

	TCHAR szPath[MAX_PATH];
	if(GetTempPath(sizeof(szPath) / sizeof(szPath[0]), szPath))
	{
		TCHAR szFile[MAX_PATH];
		GetTempFileName(szPath, _T("HD3DMAP"), 0, szFile);

		m_hFile = CreateFile(szFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL);
		if (m_hFile == INVALID_HANDLE_VALUE)
		{
			m_hFile = NULL;
			goto _end;
		}

		m_hFileMapping = CreateFileMapping(m_hFile, NULL, PAGE_READWRITE, 0, sizeof(FMH_Capture), NULL);
		if(NULL == m_hFileMapping) goto _end;

		m_hrgn = CreateRectRgn(0, 0, 0, 0);
		if (NULL == m_hrgn) goto _end;

		m_rpb.hCaptor = (HCAPTOR)this;
		m_rpb.hMutex = GetMutex();
		m_rpb.hFileMapping = m_hFileMapping;
		m_rpb.hWndNotified = g_hwndFocus;

		DebugInfoTempA("Captor(%08X) created: %s", this, m_szDeviceName);
		return TRUE;
	}
_end:
	if(m_hFileMapping) 
	{
		CloseHandle(m_hFileMapping);
		m_hFileMapping = NULL;
	}

	if(m_hMutex)
	{
		CloseHandle(m_hMutex);
		m_hMutex = NULL;
	}

	return FALSE;
}

void CCaptor::Destroy()
{
	WaitForSingleObject(m_hMutex, INFINITE);

	if (m_pSwapChain)
	{
		IDirect3DSurface9* pBackBuffer = NULL;
		m_pSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);

		if(pBackBuffer)
		{
			DebugInfoTempW(_T("Destroy D3D Private Data."));

			pBackBuffer->FreePrivateData(DID_CAPTOR);
			//Sleep(10000);
			pBackBuffer->Release();
			m_pSwapChain = NULL;
		}
	}

	if(m_pDXGISwapChain)
	{
		ID3D10Texture2D * pTexture = NULL;
		HRESULT hr = m_pDXGISwapChain->lpVtbl->GetBuffer((IDXGISwapChain*)m_pDXGISwapChain, 
			0, __uuidof(pTexture), reinterpret_cast<void**>(&pTexture));

		pTexture->SetPrivateData(DXGI_DEST_TEXTURE, 0, NULL);
		pTexture->Release();
	}

	if (m_hrgn)
	{
		DeleteObject(m_hrgn);
		m_hrgn = NULL;
	}

	if (m_pcap)
	{
		UnmapViewOfFile(m_pcap);
		m_pcap = NULL;
	}

	if (m_hFileMapping)
	{
		CloseHandle(m_hFileMapping);
		m_hFileMapping = NULL;
	}

	if (m_hFile)
	{
		CloseHandle(m_hFile);
		m_hFile = NULL;
	}

	ReleaseMutex(m_hMutex);
	if (m_hMutex)
	{
		CloseHandle(m_hMutex);
		m_hMutex = NULL;

		DebugInfoTempA("Captor(%08X) destroyed: %s", this, m_szDeviceName);
	}
}

BOOL CCaptor::Capture10()
{
	static int nIndex = 0;
	static int nUseFullScreen = 0;
	HRESULT hr;
	WaitForSingleObject(m_hMutex, INFINITE);

	if (!m_pDXGISwapChain)
	{
		ReleaseMutex(m_hMutex);
		return FALSE;
	}

	ID3D10Texture2D * pTexture = NULL;

	m_pDXGISwapChain->lpVtbl->GetBuffer((IDXGISwapChain*)m_pDXGISwapChain, 0, __uuidof(pTexture), reinterpret_cast<void**>(&pTexture));
//	DebugInfoTempA("get texture: %x", pTexture);
	if(pTexture)
	{
		D3D10_TEXTURE2D_DESC textDesc;
		pTexture->GetDesc(&textDesc);

		ID3D10Device* pDevice = NULL;
		pTexture->GetDevice(&pDevice);

		ID3D10RenderTargetView* pView = NULL;
		pDevice->OMGetRenderTargets(1, &pView, NULL);

		ID3D10Resource* pRes = NULL;
		pView->GetResource(&pRes);

		DebugInfoTempA("Swap Chain Present: %x, %x, %x", pTexture, pRes, pDevice);

		if(pView) pView->Release();
		if(pRes) pRes->Release();

		if(m_pTexture)
		{
			RECT rc = {0, 0, textDesc.Width, textDesc.Height};

			if(pRes == pTexture)
			{
				if(nUseFullScreen == 1)
					nUseFullScreen = 0;
				else
				{
					D3D10_RECT drect[3];
					UINT unScissorRect=3;
//					pDevice->RSGetScissorRects(&unScissorRect, drect);
//					rc = drect[0];
				}

//				DebugInfoTempA("Device: %x, ScissorRect: %d", pDevice, unScissorRect);
			}
			else
			{
				nUseFullScreen = 1;
				DebugInfoTempA("Use full screen");
			}

//			if(nUseFullScreen)
//			AddDirty10(&rc, _T("Present"));
			
//			if((m_dwLastCaptureTime == 0)||(GetTickCount()-m_dwLastCaptureTime>=CAPTURE_SEQUENCY))
			{
				D3D10_BOX d3dbox;
				d3dbox.back = 0;
				d3dbox.bottom = 0;
				d3dbox.left = rc.left;
				d3dbox.top = rc.top;
				d3dbox.right = rc.right;
				d3dbox.bottom = rc.bottom;

				pDevice->CopySubresourceRegion(m_pTexture, D3D10CalcSubresource(0, 0, 1),
					rc.left, rc.top, 0, pTexture, D3D10CalcSubresource(0, 0, 1),&d3dbox);

				DebugInfoTempA("sr: %d, %d, %d, %d, bmp: %d", rc.left, rc.top, 
					rc.right, rc.bottom, nIndex);

//				pDevice->CopyResource(m_pTexture, pTexture);

				LPBYTE pBits = ((LPBYTE)m_pcap)+m_pcap->dwOffsetImage;
				int nWidth = rc.right - rc.left;
				int nHeight = rc.bottom - rc.top;

				D3D10_MAPPED_TEXTURE2D mappedTex = {0};
				hr = m_pTexture->Map( D3D10CalcSubresource(0, 0, 1), D3D10_MAP_READ, 0, &mappedTex );
				
				BitBltBox32(pBits, textDesc.Width, textDesc.Height, 
					rc.left, rc.top,
					(LPBYTE)mappedTex.pData, textDesc.Width, textDesc.Height, 
					rc.left, rc.top, nWidth, nHeight);

/*				LPBYTE pNewBits = new BYTE[nWidth*nHeight*4];
				BitBltBox32(pNewBits, nWidth, nHeight, 0, 0,
					(LPBYTE)mappedTex.pData, textDesc.Width, textDesc.Height, 
					rc.left, rc.top, nWidth, nHeight);


				TCHAR szPath[MAX_PATH];
				BITMAPINFO bi;
				ZeroMemory(&bi, sizeof(bi));
				bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
				bi.bmiHeader.biBitCount = 32;
				bi.bmiHeader.biHeight = -(LONG)nHeight;
				bi.bmiHeader.biWidth = nWidth;
				bi.bmiHeader.biPlanes = 1;
				bi.bmiHeader.biCompression = BI_RGB;
				bi.bmiHeader.biSizeImage = nWidth*nHeight*4;

				_stprintf_s(szPath, TEXT("d:\\temp\\d3dcapture\\%d-1.bmp"), nIndex);
				CreateBMPFile(szPath, &bi, (LPBYTE)pNewBits);
				delete []pNewBits;
/*
				ZeroMemory(&bi, sizeof(bi));
				bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
				bi.bmiHeader.biBitCount = 32;
				bi.bmiHeader.biHeight = -(LONG)textDesc.Height;
				bi.bmiHeader.biWidth = textDesc.Width;
				bi.bmiHeader.biPlanes = 1;
				bi.bmiHeader.biCompression = BI_RGB;
				bi.bmiHeader.biSizeImage = mappedTex.RowPitch*textDesc.Height;

				_stprintf_s(szPath, TEXT("d:\\temp\\d3dcapture\\%d.bmp"), nIndex);
				CreateBMPFile(szPath, &bi, (LPBYTE)pBits);*/

				m_pTexture->Unmap(D3D10CalcSubresource(0, 0, 1));

				nIndex ++;

				RGNDATA* prd = (RGNDATA*)((PBYTE)m_pcap + m_pcap->dwOffsetRegion);
				HRGN hrgn = ExtCreateRegion(NULL, prd->rdh.dwSize + prd->rdh.nRgnSize, prd);
				if(hrgn)
				{
					if (CombineRgn(hrgn, hrgn, m_hrgn, RGN_OR)>NULLREGION)
					{
						GetRegionData(hrgn, 700, prd);
						DeleteObject(hrgn);
						m_pcap->bDirty = TRUE;
					}
				}

//				DebugInfoTempA("Dump bmp(%d): %d, %d, %d, %d", prd->rdh.nCount,
//					prd->rdh.rcBound.left, prd->rdh.rcBound.top,
//					prd->rdh.rcBound.right, prd->rdh.rcBound.bottom);

				SetRectRgn(m_hrgn, 0, 0, 0, 0);
				m_dwLastCaptureTime = GetTickCount();
			}
		}

		if(pDevice) pDevice->Release();
		pTexture->Release();
	}

	ReleaseMutex(m_hMutex);
	return TRUE;
}

BOOL CCaptor::ConnectDevice10(IDXGISwapChainDWM* pDXGISwapChain, ID3D10Device* pDevice)
{
	BOOL bConnect = FALSE;

	WaitForSingleObject(m_hMutex, INFINITE);

	if (m_pDXGISwapChain)
	{
		DisconnectDevice();
	}
/*
	DXGI_SWAP_CHAIN_DESC desc;
	pDXGISwapChain->lpVtbl->GetDesc(pSwapChain, &desc);
	DebugInfoTempA("Chain desc: %x, %d, %d, %d, %d, %d, %d", desc.OutputWindow, desc.Windowed,
		desc.BufferCount, desc.BufferDesc.Height, desc.BufferDesc.Width,
		desc.SwapEffect, desc.Flags);
*/
	ID3D10Texture2D * pTexture = NULL;

	pDXGISwapChain->lpVtbl->GetBuffer((IDXGISwapChain*)pDXGISwapChain, 0, __uuidof(pTexture), reinterpret_cast<void**>(&pTexture));
	if(pTexture)
	{
		D3D10_TEXTURE2D_DESC textDesc;
		pTexture->GetDesc(&textDesc);

		DebugInfoTempA("Texture[%x, %d]:%d, %d, %d, %d, %d, %d, %d, %d, %d", 
			textDesc.CPUAccessFlags, textDesc.Usage, 
			textDesc.Height, textDesc.Width, textDesc.Format,
			textDesc.BindFlags,textDesc.MiscFlags, textDesc.MipLevels,
			textDesc.ArraySize, textDesc.SampleDesc.Count, textDesc.SampleDesc.Quality);

//		pTexture->GetDevice(&pDevice);
		

/*		UINT unViewPorts = 3;
		D3D10_VIEWPORT vp[3];
		pDevice->RSGetViewports(&unViewPorts, vp);
		DebugInfoTempA("ViewPort: %d vp: %d, %d, %d, %d", 
			unViewPorts, vp[0].TopLeftX, vp[0].TopLeftY, vp[0].Width, vp[0].Height);

		D3D10_RECT drect[3];
		UINT unScissorRect=3;
		pDevice->RSGetScissorRects(&unScissorRect, drect);
		DebugInfoTempA("ScissorRect: %d vp: %d, %d, %d, %d", 
			unScissorRect, drect[0].left, drect[0].top, 
			drect[0].right, drect[0].bottom);*/

		textDesc.CPUAccessFlags = D3D10_CPU_ACCESS_READ;
		textDesc.Usage = D3D10_USAGE_STAGING;
		textDesc.BindFlags = 0;
		HRESULT hr = pDevice->CreateTexture2D(&textDesc,NULL, &m_pTexture);

		D3D10_MAPPED_TEXTURE2D mappedTex = {0};
		hr = m_pTexture->Map( D3D10CalcSubresource(0, 0, 1), D3D10_MAP_READ, 0, &mappedTex );
		m_pTexture->Unmap(D3D10CalcSubresource(0, 0, 1));

		DebugInfoTempA("Create new texture:%x, %x, %x, buf: %x, %d ", 
			pDevice, m_pTexture, hr, mappedTex.pData, mappedTex.RowPitch);

		DWORD dwSize = CAPTURE_HEADER_SIZE+textDesc.Width*textDesc.Height*4;

		if(m_hFileMapping) CloseHandle(m_hFileMapping);
		m_hFileMapping = CreateFileMapping(m_hFile, NULL, PAGE_READWRITE, 0, dwSize, NULL);

		if(m_hFileMapping)
			m_pcap = (FMH_Capture*)MapViewOfFile(m_hFileMapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
		
		if(m_pcap)
		{
			memcpy(&m_pcap->rpbCreate, &m_rpb, sizeof(RPB_CreateCaptor));

			m_pcap->dwOffsetRegion = sizeof(FMH_Capture);
			m_pcap->dwOffsetImage = CAPTURE_HEADER_SIZE;

			m_pcap->dwHeight = textDesc.Height;
			m_pcap->dwWidth = textDesc.Width;
			m_pcap->dxfmt.dxgifmt = textDesc.Format;
			m_pcap->bDirty = FALSE;
			m_pcap->dwSize = dwSize;
			m_pcap->hFileMapping = m_hFileMapping;
			m_pcap->iPitch = mappedTex.RowPitch;

			RGNDATA* pRgnData = (RGNDATA*)((PBYTE)m_pcap + m_pcap->dwOffsetRegion);
			SetRectRgn(m_hrgn, 0, 0, 0, 0);

			DWORD dwCount = GetRegionData(m_hrgn, 0, 0);
			GetRegionData(m_hrgn, dwCount, pRgnData);
			pRgnData->rdh.nCount = 1;
			pRgnData->rdh.nRgnSize = sizeof(RECT);
			ZeroMemory(pRgnData->Buffer, sizeof(RECT));

			RECT rc = {0, 0, textDesc.Width, textDesc.Height};
			AddDirty10(&rc, TEXT("Init"));

			m_pDXGISwapChain = pDXGISwapChain;
			m_pDevice = pDevice;
			DebugInfoTempA("Dx chain: %x", m_pDXGISwapChain);

			bConnect = TRUE;
		}
	}

	if(pTexture) pTexture->Release();

	ReleaseMutex(m_hMutex);
	return bConnect;
}

BOOL CCaptor::AddDirty10(LPRECT prc, LPTSTR pAdd)
{
	WaitForSingleObject(m_hMutex, INFINITE);

	static RECT rc = {0,0,0,0};
	static TCHAR sAdd[32];

	BOOL bAdd = FALSE;

	RECT rct;
	GetRgnBox(m_hrgn, &rct);
	if(!IsRectEmpty(&rc)&&!IsRectEmpty(&rct))
	{
		if(!EqualRect(&rc,prc))
		{
			DebugInfoTempW(_T("AddDirty10[%s]: (%d, %d - %d, %d)"), pAdd, prc->left, prc->top, prc->right, prc->bottom);

			DebugInfoTempW(_T("AddDirty10 - good: new rect"));
			rc = *prc;
			lstrcpy(sAdd, pAdd);
		}
	}

	HRGN hrgn = CreateRectRgnIndirect(prc);
	if (hrgn)
	{
		bAdd = (CombineRgn(m_hrgn, m_hrgn, hrgn, RGN_OR) != ERROR);
		DeleteObject(hrgn);
	}

	ReleaseMutex(m_hMutex);

	return bAdd;
}

BOOL CCaptor::ConnectDevice9(IDirect3DSwapChain9* pSwapChain, CDeviceHookerBase* pDeviceHooker)
{
	WaitForSingleObject(m_hMutex, INFINITE);

	if (m_pSwapChain)
	{
		IDirect3DSwapChain9* pOldSwapChain = m_pSwapChain;
		m_pSwapChain = NULL;
		ReleaseMutex(m_hMutex);

		IDirect3DSurface9* pBackBuffer = NULL;
		pOldSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
		if (pBackBuffer)
		{
			pBackBuffer->FreePrivateData(DID_CAPTOR);
			pBackBuffer->Release();
		}

		WaitForSingleObject(m_hMutex, INFINITE);
	}

	HRESULT hr;
	IDirect3DSurface9* pBackBuffer = NULL;
	pSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);

	D3DSURFACE_DESC d3dsd;
	pBackBuffer->GetDesc(&d3dsd);

	DebugInfoTempW(_T("BackBuffer(%08X): %d, %d, %d, %d, %d, %d, %d, %d"), pBackBuffer, 
		d3dsd.Format, d3dsd.Type, d3dsd.Usage, 
		d3dsd.Pool, d3dsd.MultiSampleType, 
		d3dsd.MultiSampleQuality, 
		d3dsd.Width, d3dsd.Height);

	IDirect3DDevice9* pDevice;
	pSwapChain->GetDevice(&pDevice);
	if (!pDevice)
	{
		pBackBuffer->Release();
		ReleaseMutex(m_hMutex);
		return FALSE;
	}

	IDirect3DSurface9* pSurface;
	hr = pDevice->CreateOffscreenPlainSurface(d3dsd.Width, d3dsd.Height, d3dsd.Format, D3DPOOL_SYSTEMMEM, &pSurface, NULL);
	//hr = pDevice->CreateRenderTarget(d3dsd.Width, d3dsd.Height, d3dsd.Format, D3DMULTISAMPLE_NONE, 0, TRUE, &pSurface, NULL);
	DebugInfoTempW(_T("CreateSurface: %08X"), hr);

	pDevice->Release();
	if (!pSurface)
	{
		pBackBuffer->Release();
		ReleaseMutex(m_hMutex);
		return FALSE;
	}

	m_pcap->dxfmt.d3dfmt = d3dsd.Format;
	m_pcap->dwWidth = d3dsd.Width;
	m_pcap->dwHeight = d3dsd.Height;

	D3DLOCKED_RECT d3dlr;
	RECT rc = {0, 0, 1, 1};
	hr = pSurface->LockRect(&d3dlr, &rc, D3DLOCK_READONLY);
	if (FAILED(hr))
	{
		pSurface->Release();
		pBackBuffer->Release();
		ReleaseMutex(m_hMutex);
		return FALSE;
	}

	m_pcap->iPitch = d3dlr.Pitch;

	pSurface->UnlockRect();
	pSurface->Release();

	m_pcap->dwOffsetImage = sizeof(*m_pcap);
	m_pcap->dwOffsetRegion = m_pcap->dwOffsetImage + m_pcap->iPitch * m_pcap->dwHeight;

	if(m_hFileMapping) CloseHandle(m_hFileMapping);
	m_hFileMapping = CreateFileMapping(m_hFile, NULL, PAGE_READWRITE, 0, 
		m_pcap->dwOffsetRegion + sizeof(RGNDATAHEADER) + sizeof(RECT) * ((m_pcap->dwWidth * m_pcap->dwHeight + 1) / 2), 
		NULL);

	if (!m_hFileMapping)
	{
		pBackBuffer->Release();
		ReleaseMutex(m_hMutex);
		return FALSE;
	}

	if(m_pcap) UnmapViewOfFile(m_pcap);
	m_pcap = (FMH_Capture*)MapViewOfFile(m_hFileMapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
	if (!m_pcap)
	{
		CloseHandle(m_hFileMapping);
		m_hFileMapping = NULL;
		pBackBuffer->Release();
		ReleaseMutex(m_hMutex);
		return FALSE;
	}

	m_pcap->hFileMapping = m_hFileMapping;

	RGNDATA* pRgnData = (RGNDATA*)((PBYTE)m_pcap + m_pcap->dwOffsetRegion);
	SetRectRgn(m_hrgn, 0, 0, 0, 0);

	DWORD dwCount = GetRegionData(m_hrgn, 0, 0);
	GetRegionData(m_hrgn, dwCount, pRgnData);
	pRgnData->rdh.nCount = 1;
	pRgnData->rdh.nRgnSize = sizeof(RECT);
	ZeroMemory(pRgnData->Buffer, sizeof(RECT));

	AddDirty(pBackBuffer, NULL);

	pBackBuffer->Release();

	m_pSwapChain = pSwapChain;
	m_pDeviceHooker = dynamic_cast<CDeviceHookerDrawPrimitive9*>(pDeviceHooker);

	ReleaseMutex(m_hMutex);
	return TRUE;
}

void CCaptor::DisconnectDevice()
{
	WaitForSingleObject(m_hMutex, INFINITE);

	if(m_pDXGISwapChain)
	{
		ID3D10Texture2D * pTexture = NULL;
		HRESULT hr = m_pDXGISwapChain->lpVtbl->GetBuffer((IDXGISwapChain*)m_pDXGISwapChain, 
			0, __uuidof(pTexture), reinterpret_cast<void**>(&pTexture));

		pTexture->SetPrivateData( DXGI_DEST_TEXTURE, 0, NULL);
		pTexture->Release();
		m_pDXGISwapChain = NULL;

	}

	if(m_pDevice)
	{
		m_pDevice->Release();
		m_pDevice = NULL;
	}

	m_pDeviceHooker = NULL;
	m_pSwapChain = NULL;

	ZeroMemory(m_pcap, sizeof(FMH_Capture));
	
	if(m_pTexture){ m_pTexture->Release(); m_pTexture = NULL;}

	SetRectRgn(m_hrgn, 0, 0, 0, 0);
	ReleaseMutex(m_hMutex);
}

void CCaptor::OnFinal()
{
	DebugInfoTempW(_T("Captor(%08X) OnFinal %x, %x"), this, m_pmgr, m_pDeviceHooker);

	if(m_pDeviceHooker)
		m_pmgr->DisconnectCaptorAndDevice(this, *m_pDeviceHooker);
	else
		m_pmgr->DisconnectCaptorAndDevice(this, m_pDevice);
}

BOOL CCaptor::AddDirty(IDirect3DSurface9* pSurface, RECT* prc)
{
	WaitForSingleObject(m_hMutex, INFINITE);

	BOOL bAdd = FALSE;
	RECT rc = {};
	if (!prc)
	{
		D3DSURFACE_DESC d3dsd;
		pSurface->GetDesc(&d3dsd);
		rc.right = d3dsd.Width;
		rc.bottom = d3dsd.Height;
		prc = &rc;
	}

	DebugInfoTempW(_T("AddDirty: %08X(%d, %d - %d, %d)"), pSurface, prc->left, prc->top, prc->right, prc->bottom);

	HRGN hrgn = CreateRectRgnIndirect(prc);
	if (hrgn)
	{
		bAdd = (CombineRgn(m_hrgn, m_hrgn, hrgn, RGN_OR) != ERROR);
		DeleteObject(hrgn);
	}
	//IDirect3DDevice9* pDevice;
	//pSurface->GetDevice(&pDevice);
	//HRESULT hr = pDevice->SetRenderTarget(2, pSurface);
	//pDevice->Release();
	//DebugInfoTemp("SetRT(%08X): %08X", pSurface, hr);
	ReleaseMutex(m_hMutex);

	return bAdd;
}

BOOL CCaptor::Capture()
{
	HRESULT hr;
	WaitForSingleObject(m_hMutex, INFINITE);

	DebugInfoTempW(_T("Capture: %08X"), m_pSwapChain);
	if (!m_pSwapChain)
	{
		ReleaseMutex(m_hMutex);
		return FALSE;
	}

	RGNDATA* prd = (RGNDATA*)((PBYTE)m_pcap + m_pcap->dwOffsetRegion);
	HRGN hrgn = ExtCreateRegion(NULL, prd->rdh.dwSize + prd->rdh.nRgnSize, prd);
	if (!hrgn)
	{
		DebugInfoTempW(_T("Create Region Failed"));
		ReleaseMutex(m_hMutex);
		return FALSE;
	}

	//sams, it's empty if the last call is present
	DWORD dwCount;
	dwCount = GetRegionData(m_hrgn, 0, 0);
	GetRegionData(m_hrgn, dwCount, prd);

	RECT rcRgn;
	GetRgnBox(m_hrgn, &rcRgn);
	DebugInfoTempW(_T("m_hrgn box: %d, %d, %d, %d"), rcRgn.left, rcRgn.top,
		rcRgn.right, rcRgn.bottom);

	IDirect3DDevice9* pDevice;
	m_pSwapChain->GetDevice(&pDevice);

	IDirect3DSurface9* pBackBuffer = NULL;
	m_pSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
	if (!prd->rdh.nCount)
	{
		AddDirty(pBackBuffer, NULL);
		dwCount = GetRegionData(m_hrgn, 0, 0);
		GetRegionData(m_hrgn, dwCount, prd);
	}

	D3DSURFACE_DESC d3dsd;
	pBackBuffer->GetDesc(&d3dsd);

	IDirect3DSurface9* pSurface;
	hr = pDevice->CreateOffscreenPlainSurface(d3dsd.Width, d3dsd.Height, d3dsd.Format, D3DPOOL_SYSTEMMEM, &pSurface, NULL);
	DebugInfoTempW(_T("GetRTD>"));

	hr = pDevice->GetRenderTargetData(pBackBuffer, pSurface);
	//for (int i = 0; i < prd->rdh.nCount; i++)
	//{
	//RECT* prc = (RECT*)prd->Buffer + i;
	//hr = pDevice->StretchRect(pBackBuffer, prc, pSurface, prc, D3DTEXF_NONE);
	//}
	//Sleep(500);
	DebugInfoTempW(_T("GetRTD %08X->%08X: %08X"), pBackBuffer, pSurface, hr);

	pBackBuffer->Release();
	pDevice->Release();
	//HDC hdcScr;
	//hdcScr = CreateDCA("DISPLAY", m_szDeviceName, NULL, NULL);
	//DebugInfoTemp("ScrDC: %08X", hdcScr);
	//HDC hdcMem = NULL;
	//pSurface->GetDC(&hdcMem);
	//DebugInfoTemp("MemDC: %08X", hdcMem);
	////BitBlt(hdcMem, 0, 0, m_pcap->dwWidth, m_pcap->dwHeight, hdcScr, 0, 0, SRCCOPY);
	//LineTo(hdcScr, m_pcap->dwWidth, m_pcap->dwHeight);
	//pSurface->ReleaseDC(hdcMem);
	//DeleteDC(hdcScr);

	D3DLOCKED_RECT d3dlr;
	pSurface->LockRect(&d3dlr, NULL, D3DLOCK_READONLY);
	//memcpy((PBYTE)m_pcap + m_pcap->dwOffsetImage, d3dlr.pBits, m_pcap->iPitch * m_pcap->dwHeight);
	RECT* prc = (RECT*)prd->Buffer;
	DWORD dwBytes = 0;
	for (UINT i = 0; i < prd->rdh.nCount; i++)
	{
		BYTE* pb0 = (PBYTE)d3dlr.pBits + d3dlr.Pitch * prc->top + 4 * prc->left;
		BYTE* pb1 = (PBYTE)m_pcap + m_pcap->dwOffsetImage + m_pcap->iPitch * prc->top + 4 * prc->left;
		DWORD dwLen = 4 * (prc->right - prc->left);
		for (int y = prc->top; y < prc->bottom; y++)
		{
			memcpy(pb1, pb0, dwLen);
			pb0 += d3dlr.Pitch;
			pb1 += m_pcap->iPitch;
			dwBytes += dwLen;
		}
		prc++;
	}

	DebugInfoTempW(_T("Total Copied: %d"), dwBytes);

	pSurface->UnlockRect();
	pSurface->Release();

	if (CombineRgn(hrgn, hrgn, m_hrgn, RGN_OR) ==ERROR)
	{
		DebugInfoTempW(_T("Combine Region Failed"));

		DeleteObject(hrgn);
		ReleaseMutex(m_hMutex);
		return FALSE;
	}

	dwCount = GetRegionData(hrgn, 0, 0);
	GetRegionData(hrgn, dwCount, prd);
	DeleteObject(hrgn);

	SetRectRgn(m_hrgn, 0, 0, 0, 0);
	m_pcap->bDirty = TRUE;

	ReleaseMutex(m_hMutex);
	return TRUE;
}

HRESULT STDMETHODCALLTYPE CCaptor::IDirect3DDevice9DrawPrimitive(IDirect3DDevice9* This, D3DPRIMITIVETYPE PrimitiveType,UINT StartVertex,UINT PrimitiveCount)
{
	DebugInfoTempW(_T("IDirect3DDevice9DrawPrimitive(%08X): %d, %d, %d"), This,  PrimitiveType, StartVertex, PrimitiveCount);

	HRESULT hr;

	IDirect3DSurface9* pRenderTarget;
	This->GetRenderTarget(0, &pRenderTarget);

	DWORD dwSize;
	CCaptor* pCaptor;
	CDeviceHookerDrawPrimitive9* pDeviceHooker;

	hr = pRenderTarget->GetPrivateData(DID_CAPTOR, &pCaptor, &dwSize);
	if (FAILED(hr))
	{
		pRenderTarget->Release();
		pDeviceHooker = g_mgr.GetDeviceHooker9(This);
		if (!pDeviceHooker)
		{
			DebugInfoTempW(_T("GetDeviceHooker failed!!!"));
			return S_OK;
		}
		return (*pDeviceHooker)(This, PrimitiveType, StartVertex, PrimitiveCount);
	}

	DebugInfoTempW(_T("Device(%08X)DrawPrimitive"), This);

	D3DVIEWPORT9 d3dvp = {};
	This->GetViewport(&d3dvp);

	RECT rc;
	rc.left = d3dvp.X;
	rc.top = d3dvp.Y;
	rc.right = d3dvp.X + d3dvp.Width;
	rc.bottom = d3dvp.Y + d3dvp.Height;

	pDeviceHooker = pCaptor->m_pDeviceHooker;
	hr = (*pDeviceHooker)(This, PrimitiveType, StartVertex, PrimitiveCount);

	pCaptor->AddDirty(pRenderTarget, &rc);

	pCaptor->Release();
	pRenderTarget->Release();
	return hr;
}

void STDMETHODCALLTYPE CCaptor::ID3D10Draw(ID3D10Device* pThis, UINT VertexCount, UINT StartVertexLocation)
{
//	g_mgr.m_cs.Enter();

	ID3D10RenderTargetView* pView = NULL;
	pThis->OMGetRenderTargets(1, &pView, NULL);

	ID3D10Resource* pRes = NULL;
	pView->GetResource(&pRes);

	CCaptor* pSwapCaptor = NULL;
	UINT unSize = sizeof(pSwapCaptor);
	HRESULT hr = pRes->GetPrivateData(DXGI_DEST_TEXTURE, &unSize, &pSwapCaptor);
	if(SUCCEEDED(hr))
	{
//		DebugInfoTempA("D3D10 Draw: %x, %d, %d", pThis, VertexCount, StartVertexLocation);
		D3D10_RECT drect[3];
		UINT unScissorRect=3;
		pThis->RSGetScissorRects(&unScissorRect, drect);
//		DebugInfoTempA("ScissorRect: %d vp: %d, %d, %d, %d", 
//			unScissorRect, drect[0].left, drect[0].top, 
//			drect[0].right, drect[0].bottom);
		pSwapCaptor->AddDirty10(&drect[0], _T("Draw"));
		pSwapCaptor->Release();
	}

	if(pView) pView->Release();
	if(pRes) pRes->Release();

	CDeviceHookerDraw10* pHookDraw = dynamic_cast<CDeviceHookerDraw10*>
										(g_mgr.GetDeviceHooker10(pThis, HOOK_DRAW10));
	if(pHookDraw)
	{
		(*pHookDraw)(pThis, VertexCount, StartVertexLocation);
	}

//	DebugInfoTempA("D3D10 Draw original: %x, %d, %d", pHookDraw, VertexCount, StartVertexLocation);

//	g_mgr.m_cs.Leave();
}

void STDMETHODCALLTYPE CCaptor::ID3D10DrawIndexed(ID3D10Device* pThis, UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation)
{

	ID3D10RenderTargetView* pView = NULL;
	pThis->OMGetRenderTargets(1, &pView, NULL);

	ID3D10Resource* pRes = NULL;
	pView->GetResource(&pRes);

	CCaptor* pSwapCaptor = NULL;
	UINT unSize = sizeof(pSwapCaptor);
	HRESULT hr = pRes->GetPrivateData(DXGI_DEST_TEXTURE, &unSize, &pSwapCaptor);
	if(SUCCEEDED(hr))
	{
//		DebugInfoTempA("D3D10 DrawIndexed: %x, %d, %d", pThis, IndexCount, StartIndexLocation, BaseVertexLocation);
		D3D10_RECT drect[3];
		UINT unScissorRect=3;
		pThis->RSGetScissorRects(&unScissorRect, drect);
		pSwapCaptor->AddDirty10(&drect[0], _T("DrawIndexed"));
		pSwapCaptor->Release();
	}

	if(pView) pView->Release();
	if(pRes) pRes->Release();

	CDeviceHookerDrawIndexed10* pHookDraw = dynamic_cast<CDeviceHookerDrawIndexed10*>
		(g_mgr.GetDeviceHooker10(pThis, HOOK_DRAWINDEXED10));
	if(pHookDraw)
	{
		(*pHookDraw)(pThis, IndexCount, StartIndexLocation, BaseVertexLocation);
	}
}